package com.nys.conci.backend.servicio;

import com.nys.conci.backend.entidad.EnumParametros;
import com.nys.conci.base.utilitario.Autenticador;
import com.nys.conci.base.utilitario.UTF8;
import com.nys.conci.base.utilitario.UtilPlantillaCorreo;
import com.nys.conci.base.ServicioBase;
import java.io.ByteArrayInputStream;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

/**
 * Clase utilitaria para facilitar la conexion, creacion y envio de correo de la
 * aplicacion, para las notificaciones del sistema.
 *
 * <br/><br/><i>Por ahora esta configurado para enviar correos por medio de una
 * cuenta de correo de la ucr, por medio de SMTP</i>
 *
 * @author Adam M. Gamboa González
 * @author Sergio Vega Fuentes (Modificador).
 * @see Autenticador
 */
@Stateless
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
public class ServicioCorreo extends ServicioBase {

    /**
     * Se almacena el asunto del correo.
     */
    private String asunto_correo;
    /**
     * Almacena el cuerpo del correo.
     */
    private String cuerpo;
    /**
     * Almacena las direcciones de los adjuntos.
     */
    private List<String> adjuntos_direccion;
    /**
     * Almacena los adjuntos asociados al correo en bytes.
     */
    private HashMap<String, byte[]> adjuntos;
    /**
     * Almacena las direcciones de los destinatarios.
     */
    private HashMap<Message.RecipientType, InternetAddress[]> destinatarios;
    /**
     * Almacena la sesión para el envío de correos.
     */
    private Session session;
    /**
     * Contiene el contenido total del correo.
     */
    private MimeMessage mensaje;
    /**
     * Contructor de la clase.<br/> Envia un correo sin documento adjunto.
     *
     * @param asunto Asunto asociado al correo.
     * @param cuerpo Contenido del Documento.
     * @param destinarios Lista de destinatarios.
     */
    
    @Inject 
    private ServicioParametros servicioParametros;

    public ServicioCorreo() {
    }

    @PostConstruct
    public void inicializarPost() {
    }

    public void initServicioCorreo(String asunto, String cuerpo, HashMap<Message.RecipientType, InternetAddress[]> destinarios) {
        try {
            this.asunto_correo = UTF8.aUTF8(asunto);
            this.cuerpo = UTF8.aUTF8(cuerpo);
            this.destinatarios = destinarios;
            this.adjuntos_direccion = new ArrayList<String>();
            this.adjuntos = new HashMap<String, byte[]>();
            configurarCorreo();
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Constructor de la clase.<br/> Se envía un correo con la ruta de los
     * documentos adjuntos.
     *
     * @param asunto Asunto del correo.
     * @param cuerpo Contenido del correo.
     * @param destinarios Lista de destinatarios.
     * @param adjuntos Documentos adjuntos.
     */
    public void initServicioCorreo(String asunto, String cuerpo, HashMap<Message.RecipientType, InternetAddress[]> destinarios, List<String> adjuntos) {
        try {
            this.asunto_correo = UTF8.aUTF8(asunto);
            this.cuerpo = UTF8.aUTF8(cuerpo);
            this.destinatarios = destinarios;
            if (adjuntos != null) {
                this.adjuntos_direccion = adjuntos;
            } else {
                this.adjuntos_direccion = new ArrayList<String>();
            }
            this.adjuntos = new HashMap<String, byte[]>();
            configurarCorreo();
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Constructor de la clase.<br/> Se envía un correo con documento ajuntos en
     * formato binario.
     *
     * @param asunto Asunto del correo.
     * @param cuerpo Cuerpo del correo.
     * @param destinarios Lista de destinatarios.
     * @param adjuntos Documentos adjuntos.
     */
    public void initServicioCorreo(String asunto, String cuerpo, HashMap<Message.RecipientType, InternetAddress[]> destinarios, HashMap<String, byte[]> adjuntos) {
        try {
            this.asunto_correo = UTF8.aUTF8(asunto);
            this.cuerpo = UTF8.aUTF8(cuerpo);
            this.destinatarios = destinarios;
            if (adjuntos != null) {
                this.adjuntos = adjuntos;
            } else {
                this.adjuntos = new HashMap<String, byte[]>();
            }
            this.adjuntos_direccion = new ArrayList<String>();
            configurarCorreo();
        } catch (Exception ex) {
            Logger.getLogger(ServicioCorreo.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Constructor de la clase.<br/> Construye un correo mediante una platilla
     * base sin documento adjunto para un solo destinatario El parámetro
     * rutaStringTemplate es necesario unicamente para crear la plantilla fuera
     * del contexto web, como por ejemplo en un hilo. Sí el proceso se ejecutará
     * en un ambiente con contexto web mandar el parametro nulo
     *
     * @param asunto String
     * @param cuerpo String
     * @param correo String
     * @param rutaStringTemplate String
     * @since 12/09/2011
     * @author wPorras
     */
    public void initServicioCorreo(String asunto, String cuerpo, String correo, String rutaStringTemplate) {
        try {
            this.asunto_correo = UTF8.aUTF8(asunto);
            this.cuerpo = UtilPlantillaCorreo.crearPlantillaGenerica(cuerpo);
            HashMap<Message.RecipientType, InternetAddress[]> destinatarioHashMap = new HashMap<Message.RecipientType, InternetAddress[]>();
            destinatarioHashMap.put(Message.RecipientType.TO, new InternetAddress[]{new InternetAddress(correo)});
            this.destinatarios = destinatarioHashMap;
            this.adjuntos_direccion = new ArrayList<String>();
            this.adjuntos = new HashMap<String, byte[]>();
            configurarCorreo();
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }//ControlCorreo

    /**
     * Inicializa la sesión del correo y envía el correo con su respectivo
     * contenido.
     */
    public void enviarCorreo() throws Exception {
        //   if (Configuracion.HABILITADO.estado) {//Utilizado para habilitar o deshabilitar el envío de correos.
        try {
            Transport t = session.getTransport(servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.PROTOCOLO));
            t.connect(servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.HOST), Integer.parseInt(servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.PUERTO_ALTERNO)), servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CUENTA_ORIGEN), servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CONTRASENA_ORIGEN));
            t.sendMessage(mensaje, mensaje.getAllRecipients());
            t.close();

        } catch (MessagingException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
            //ex.printStackTrace();
            throw new Exception("Error enviando el correo", ex);
        } catch (Exception ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
            throw new Exception("Error enviando el correo", ex);
        }
        // }
    }

    /**
     * Configura la sesion actual del envio de correos.
     */
    private void configurarCorreo() throws Exception {
        boolean flag = false;
        try {
            session = Session.getInstance(propiedadesConexion(), new Autenticador(servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CUENTA_ORIGEN), servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CONTRASENA_ORIGEN)));
            flag = true;
            mensaje = new MimeMessage(session);
            encabezadoMensaje();
            contenidoMensaje();
        } catch (MessagingException mex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, mex);
            if (!flag) {
                throw new Exception("Error al crear la conexion para preparar el correo");
            } else {
                throw new Exception("Error al construir el correo (encabezado o Contenido)");
            }

        }
    }

    /**
     * Construye el encabezado del correo, es decir, indica quien envia el
     * correo, y cuales son todos los destinatarios que se han indicado para el
     * correo.
     *
     * @throws MessagingException
     */
    private void encabezadoMensaje() throws MessagingException {

        //Envia el mensaje
        mensaje.setFrom(new InternetAddress(servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CONTRASENA_ORIGEN)));

        for (Map.Entry<Message.RecipientType, InternetAddress[]> dest : this.destinatarios.entrySet()) {
            //Destinatario "PARA"
            if (dest.getKey().equals(Message.RecipientType.TO)) {
                mensaje.addRecipients(Message.RecipientType.TO, dest.getValue());
            }//Destinatario "COPIA"
            else if (dest.getKey().equals(Message.RecipientType.CC)) {
                mensaje.addRecipients(Message.RecipientType.CC, dest.getValue());
            } else//Destinatario "COPIA OCULTA"
            {
                mensaje.addRecipients(Message.RecipientType.BCC, dest.getValue());
            }
        }

        mensaje.setSubject(this.asunto_correo);    //Asunto del correo
        mensaje.setSentDate(new Date());    //Fecha de envio
    }

    /**
     * Construye el contenido del correo, es decir, el mensaje que se muestra
     * ademas de los archivos adjuntos que se le agregan.
     *
     * @throws MessagingException
     */
    private void contenidoMensaje() throws MessagingException {

        Multipart contenido_mensaje = new MimeMultipart();

        MimeBodyPart texto = new MimeBodyPart();
        texto.setContent(cuerpo, "text/html;charset=UTF-8");
        contenido_mensaje.addBodyPart(texto);

        try {
            for (String contenido_adjunto : adjuntos_direccion) {
                FileDataSource archivo_adjunto = new FileDataSource(contenido_adjunto);
                MimeBodyPart adjunto = new MimeBodyPart();
                adjunto.setDataHandler(new DataHandler(archivo_adjunto));
                adjunto.setFileName(archivo_adjunto.getName());
                contenido_mensaje.addBodyPart(adjunto);
            }

            for (Map.Entry<String, byte[]> adj : this.adjuntos.entrySet()) {

                MimeBodyPart adjunto = new MimeBodyPart();
                ByteArrayInputStream attach = new ByteArrayInputStream(adj.getValue());
                ByteArrayDataSource ds = new ByteArrayDataSource(attach, "application/pdf");
                adjunto.setDataHandler(new DataHandler(ds));
                adjunto.setFileName(adj.getKey());
                contenido_mensaje.addBodyPart(adjunto);
            }
            mensaje.setContent(contenido_mensaje);
        } catch (Exception e) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
        }
    }

    /**
     * Inicia los valores para la conexión con el servidor
     *
     * @return todas las propiedades para realizar la conexión
     */
    private Properties propiedadesConexion() {
        Properties propiedades = new Properties();   

        propiedades.setProperty("mail.smtp.host", servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.HOST));          //Host del servidor de correo
        propiedades.setProperty("mail.smtp.port", servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.PUERTO));    //Puerto del servidor de correo
        propiedades.setProperty("mail.smtp.user", servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.CUENTA_ORIGEN)); //Correo con el cual se va a iniciar la conexion
        propiedades.setProperty("mail.smtp.auth", servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.SMTP_AUTH));     //Indica si el servidor necesita de autentificacion
        propiedades.setProperty("mail.smtp.starttls.enable", servicioParametros.retornaValorParametroDesdeEnumParametro(EnumParametros.SMTP_STARTTLS));  //Habilitar TLS Utilizado en cuenta de Origen Gmail.com
        return propiedades;
    }
    
    /**
     * 
     */
    
}
